package com.abso.mp3tunes.locker.ui.actions;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.dialogs.ListDialog;

import com.abso.mp3tunes.locker.core.LockerServiceException;
import com.abso.mp3tunes.locker.core.data.Playlist;
import com.abso.mp3tunes.locker.core.data.Track;
import com.abso.mp3tunes.locker.ui.LockerImages;
import com.abso.mp3tunes.locker.ui.LockerPlugin;
import com.abso.mp3tunes.locker.ui.viewers.LockerViewer;
import com.abso.mp3tunes.locker.ui.viewers.PlaylistViewer;
import com.abso.mp3tunes.locker.ui.viewers.PlaylistViewerLabelProvider;
import com.abso.mp3tunes.locker.ui.views.LockerBrowserView;

/**
 * Action able to append a set of tracks to an existing playlist.
 */
public class AppendToPlaylistAction extends AbstractLockerViewerAction {

	/** The selected elements. */
	private List elements;

	/**
	 * The action able to create a new playlist, used in case no editable
	 * playlists are available.
	 */
	private AddToNewPlaylistAction addToNewPlaylistAction;

	/**
	 * Constructs a new action.
	 * 
	 * @param lockerViewer
	 *            the Locker tree viewer.
	 * @param addToNewPlaylistAction
	 *            the action able to create a new playlist, used in case no
	 *            editable playlists are available.
	 */
	public AppendToPlaylistAction(LockerViewer lockerViewer,
			AddToNewPlaylistAction addToNewPlaylistAction) {
		super("Append to Existing Playlist...", LockerImages
				.getDescriptor(LockerImages.ADD_TO_PLAYLIST), lockerViewer);
		this.addToNewPlaylistAction = addToNewPlaylistAction;
	}

	/**
	 * Sets the current selection.
	 * 
	 * @param elements
	 *            the selected elements.
	 */
	public void setSelectedElements(List elements) {
		this.elements = elements;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.jface.action.Action#run()
	 */
	public void run() {
		Shell shell = LockerPlugin.getActiveWorkbenchShell();
		Display display = shell.getDisplay();

		/* retrieves the existing playlists */
		final List playlists = new ArrayList();
		BusyIndicator.showWhile(display, new Runnable() {
			public void run() {
				try {
					playlists.addAll(Arrays.asList(LockerPlugin.getDefault()
							.getLockerService().getPlaylists()));
				} catch (LockerServiceException e) {

					/* adds the exception to the list */
					playlists.add(e);
				}
			}
		});

		/* checks if the retrieval has thrown an error */
		if (!playlists.isEmpty()
				&& (playlists.get(0) instanceof LockerServiceException)) {
			showErrorDialog("Error", "Unable to retrieve playlists",
					"Unable to retrieve playlists",
					(LockerServiceException) playlists.get(0));
			return;
		}

		/* removes the predefined playlists from the list */
		for (Iterator i = playlists.iterator(); i.hasNext();) {
			Playlist playlist = (Playlist) i.next();
			if (playlist.isReadOnly()) {
				i.remove();
			}
		}

		/*
		 * checks if the playlists just included the 3 predefined (non-editable)
		 * playlists
		 */
		if (playlists.isEmpty()) {
			if (MessageDialog
					.openQuestion(shell, "No Playlists",
							"Your locker does not include editable playlists. Ok to create a new one?")) {
				addToNewPlaylistAction.run();
			}
			return;
		}

		/* opens the selection dialog */
		ListDialog listDialog = new ListDialog(shell);
		listDialog.setTitle("Available Playlists");
		listDialog.setMessage("Select an existing playlist:");
		listDialog.setContentProvider(new ArrayContentProvider());
		listDialog.setLabelProvider(new PlaylistViewerLabelProvider());
		listDialog.setInput(playlists);
		listDialog.setInitialSelections(new Object[] { playlists.get(0) });
		if (listDialog.open() == Window.OK) {
			Object[] result = listDialog.getResult();
			if ((result != null) && (result.length > 0)) {
				appendToPlaylist((Playlist) result[0]);
			}
		}
	}

	/**
	 * Appends the selected tracks to the given playlist.
	 * 
	 * @param playlist
	 *            the playlist to be updated.
	 */
	private void appendToPlaylist(final Playlist playlist) {
		IRunnableWithProgress runnable = new IRunnableWithProgress() {
			public void run(IProgressMonitor monitor)
					throws InvocationTargetException, InterruptedException {
				Track[] tracksToAdd = unwrapTracks(elements, monitor);
				if ((tracksToAdd == null) || (tracksToAdd.length == 0)) {
					return;
				}
				try {
					monitor.beginTask("Adding tracks", tracksToAdd.length);
					for (int i = 0; i < tracksToAdd.length; i++) {
						if (monitor.isCanceled()) {
							break;
						}
						monitor.subTask(getTrackLabel(tracksToAdd[i]));
						LockerPlugin.getDefault().getLockerService()
								.appendTrackToPlaylist(playlist.getId(),
										tracksToAdd[i].getFileKey());
						monitor.worked(1);
					}
					getLockerViewer().getLockerBrowser().setViewerDirty(
							LockerBrowserView.PLAYLIST_MODE);
				} catch (final LockerServiceException e) {
					showErrorDialog("Error", "Unable to update the playlist",
							"Error updating the playlist", e);
				} finally {
					monitor.done();
				}
				runUIAsync(new Runnable() {
					public void run() {
						if (getLockerViewer() instanceof PlaylistViewer) {
							getLockerViewer().setDirty();
							getLockerViewer().updateContent();
						}
					}
				});
			}
		};
		try {
			ProgressMonitorDialog dialog = new ProgressMonitorDialog(
					LockerPlugin.getActiveWorkbenchShell()) {
				protected void configureShell(final Shell shell) {
					super.configureShell(shell);
					shell.setText("Playlist Update");
				}
			};
			dialog.run(true, true, runnable);
		} catch (InvocationTargetException e) {
		} catch (InterruptedException e) {
		}
	}

}
